﻿//#define TEST_PROGRESS_UPDATE
using System;
using System.Diagnostics;
using System.Net.Sockets;
using System.Collections.Generic;
using System.Collections;
using System.Text;
using System.Timers;
using System.IO;
using System.Threading.Tasks;
using System.ComponentModel;
using System.Threading;
using LetiLib.Data;
using LetiLib.Connection;
using LetiLib.Database;
using LetiLib.Utils;
using System.Windows;
using System.Windows.Forms;
using LetiLib.Security.Cryption;

namespace LetiLib.Device
{
    public delegate void DataTimeoutHandler(object sender, EventArgs e);
   
    public class TCPDevice : BaseDevice
    {
        public const int PACKAGE_TIMEOUT = 100000;
        // TCP info
        public string IP{get;set;}// IP
        public int Port{get;set;}

        public TcpClient _tcpClient;
        public IAsyncResult IAR;

        #region | Contructors |
        /// <summary>
        /// Default constructor
        /// </summary>
        /// <returns></returns>
        public TCPDevice():base()
        {
            IP = "0.0.0.0";
            Port = 0;
            //DataTimer.Elapsed += new ElapsedEventHandler(DataTimer_Elapsed);
            
            bwSimulator = new BackgroundWorker();
            bwSimulator.DoWork += new DoWorkEventHandler(bwSimulatorDoWork);

            bwSimulator.WorkerReportsProgress = true;
            bwSimulator.WorkerSupportsCancellation = true;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="name"></param>
        /// <param name="type"></param>
        /// <param name="pass"></param>
        /// <param name="ip"></param>
        /// <param name="port"></param>
        /// <returns></returns>
        public TCPDevice(string name, string imei,string pass, string ip, int port)
            : base(name, imei,DEVICE_TYPE.TCP, pass)
        {
            IP = ip;
            Port = port;
            _tcpClient = null;
            //DataTimer.Elapsed += new ElapsedEventHandler(DataTimer_Elapsed);

            bwSimulator = new BackgroundWorker();
            bwSimulator.DoWork += new DoWorkEventHandler(bwSimulatorDoWork);

            bwSimulator.WorkerReportsProgress = true;
            bwSimulator.WorkerSupportsCancellation = true;
        }
        #endregion

        #region | IBaseDevice implementation |
        public override string ToXML()
        {
            return "TCP xml";
        }
        #endregion

        #region | IConnectable implementation |
        /// <summary>
        /// Open connection
        /// </summary>
        /// <returns></returns>
        public override string open()
        {
            base.open();
            string result;
            try
            {
                _tcpClient = new TcpClient(IP,Port);
                //_stream = _tcpClient.GetStream();
                StateObject state = new StateObject();
                //state.workSocket = _tcpClient.Client;
                
                /*
                _tcpClient.Client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
                            new AsyncCallback(OnReceive), state);

                */
                if (!_tcpClient.Connected)
                    return "Error";

                // send client version
                /*
                result = SendData(WMAMOBILE_STX, WMA_VERSION, 1, new byte[] { 0x01 }, stream);
                if (!result.Equals(noError))
                {
                    _stream.Close();
                    return "Error";
                }
                 * */
            }
            catch (Exception exception)
            {
                return exception.Message;

            }
            result = "OK";
            return result;
        }

        /// <summary>
        /// Close connection
        /// </summary>
        /// <returns></returns>
        public override void close()
        {

            if (_tcpClient != null && _tcpClient.Connected)
            _tcpClient.Client.Disconnect(false);
            //if (_stream != null)
            //_stream.Close();
            _tcpClient = null;
        }

        /// <summary>
        /// Write data
        /// </summary>
        /// <param name="data"></param>
        /// <param name="length"></param>
        /// <returns></returns>
        public override string writeData(byte[] data, int length)
        {
            string result;
            try{
                //_stream.Write(data,0,length);
            }
            catch(Exception ex)
            {
                result = "error";

                close();
            }
            result = "OK";
            return result;
        }
        // < end of IConnectable 
        #endregion

        #region | Send Data |
        // Send data
        public void sendProgram1(ProgramData prog)
        {         /*
            programToSent = prog;
#if !SEND_PROGRAM_OLDVERSION
            TaskScheduler uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
            DateTime before = DateTime.Now;
            // Send program process
            Task programSender = Task.Factory.StartNew(() =>
            {
                try
                {
                    DataTimer.AutoReset = false;
                    DataTimer.Stop();
                    DataTimer.Start();
                    Logger.Instance.Log(this,"Sending  update data request...");
                    bDataRequested = false;
                    if (Sender.vSendUpdateDataRequest(_tcpClient.Client, 1))
                    {

                    }
                    while (!bDataRequested)
                    {
                        if (bDataRequestedTimeout)
                        {
                            Logger.Instance.Log(this,"      > Sending  update data request cancel because of timeout!");
                            return;
                        }
                    }
                    DataTimer.Stop();
                    bDataInfoSent = false;
                    //Thread.Sleep(100);
                    Logger.Instance.Log(this,"Sending  update data info...");
                    Sender.vSendUpdateDataInfo(_tcpClient.Client, prog.Info);
                    while (!bDataInfoSent)
                    {
                        if (bDataRequestedTimeout)
                        {
                            Logger.Instance.Log(this,"     > Sending  update data info canceled because of timeout!");
                            return;
                        }
                    }
                    Logger.Instance.Log(this,"Sent  update data info");

                    for (int i = 0; i < prog.Pakages.Count; i++)
                    {
                        //Thread.Sleep(100);
                        Logger.Instance.Log(this,"Sending update data package " + i);
                        bFirmwarePackageSent = false;
                        Sender.vSendUpdateDataPackage(_tcpClient.Client, prog.Pakages[i]);
                        DataTimer.Stop();
                        while (!bFirmwarePackageSent)
                        {
                            if (bFirmwarePackageSentError_checksum)
                            {
                                if (bDataRequestedTimeout) return;
                                Sender.vSendUpdateDataPackage(_tcpClient.Client, prog.Pakages[i]);
                                bFirmwarePackageSentError_checksum = false;
                            }
                        }
                    }


                }
                catch (System.Exception ex)
                {
                    Logger.Instance.Log(this,ex.Message);
                    //MessageBox.Show("Error while sending firmware to selected client\r\n" + ex.Message);
                    return;
                }
            },
                TaskCreationOptions.LongRunning
            );
            // finishes after running
            programSender.ContinueWith(task =>
            {
                // detects operation duration
                //TimeSpan duration = DateTime.Now - before;
                //TimeSpan perPixel = new TimeSpan(duration.Ticks);
                MessageBox.Show("Send program done!");
            }, uiScheduler);
#else
            if (bwSimulator.IsBusy)
                bwSimulator.CancelAsync();
            bwSimulator.RunWorkerAsync();
#endif
                   * */
        }

        public void sendProgramSync(ProgramData prog)
        {
            programToSent = prog;
               /*
            TaskScheduler uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
            DateTime before = DateTime.Now;
            // Send program process
            Task programSender = Task.Factory.StartNew(() =>
            {
                * */
            try
            {
                // First configuration
                _tcpClient.SendTimeout = 15000;
                _tcpClient.ReceiveTimeout = 15000;
                NetworkStream stream = _tcpClient.GetStream();
                stream.Flush();
                BinaryReader bReader = new BinaryReader(stream);
                BinaryWriter bWriter  = new BinaryWriter(stream);

                byte[] receivedBuffer = new byte[1024];
                int readResult = 0;
                bool sentOK = false;
                
                Logger.Instance.Log(this, "Sending  update data request...");
                byte[] data = new byte[2];
                data[0] = (byte)COMMAND.SERVER_TAG ;
                data[1] = (byte)COMMAND.CMD_SERVER_DATA_UPDATE_REQ ;
                // Send the message to the connected TcpServer. 
                //stream.Flush();
                //Sender.SendBytes(_tcpClient.Client, data);
                //bWriter = new BinaryWriter(_tcpClient.GetStream());
                bWriter.Write(XORCryptor.Encrypt(data));
                //stream.Write(data, 0, data.Length);
                // read response from device
                Thread.Sleep(20);
                //stream.Flush();
                //receivedBuffer = bReader.ReadBytes(3);
                //bReader = new BinaryReader(_tcpClient.GetStream());
                readResult = bReader.Read(receivedBuffer, 0, 3);
                bReader.BaseStream.Flush();
                //receivedBuffer = ReadFully(_tcpClient.GetStream(), 3);
                //readResult = stream.Read(receivedBuffer, 0, receivedBuffer.Length);
                if (//receivedBuffer.Length == 3 &&
                    readResult == 3 &&
                    receivedBuffer[0] == (byte)COMMAND.DEVICE_TAG &&
                    receivedBuffer[1] == (byte)COMMAND.CMD_SERVER_DATA_UPDATE_REQ)
                {
                    switch (receivedBuffer[2])
                    {
                        case (byte)DEVICE_RESPONSE.SUCCESS:
                            sentOK = true;
                            break;
                        case (byte)DEVICE_RESPONSE.ERROR:
                        default:
                            sentOK = false;
                            break;
                    }
                    Logger.Instance.Log(this, "Device response: " + (DEVICE_RESPONSE)receivedBuffer[2]);
                    if (!sentOK)
                        return;
                }
                else
                {
                    // Process error here
                    Logger.Instance.Log(this, "Device response too much data:  " + readResult + "bytes"
                        + "\n   TAG: " + receivedBuffer[0] 
                        + "\n   COMMAND: " + receivedBuffer[1] );
                    return;
                }

                Logger.Instance.Log(this, "Sending  update data info...");
                //stream.Flush();
                //Sender.SendBytes(_tcpClient.Client, prog.Info.Data);

                bWriter.Write(XORCryptor.Encrypt(prog.Info.Data));
                Thread.Sleep(20);
                //stream.Write(prog.Info.Data, 0, prog.Info.Data.Length);
                // read for response
                //stream.Flush();
                //receivedBuffer = ReadFully(stream, 3);
                //receivedBuffer = bReader.ReadBytes(3);
                //bReader = new BinaryReader(_tcpClient.GetStream());
                readResult = bReader.Read(receivedBuffer, 0, 3);
                bReader.BaseStream.Flush();
                if (//receivedBuffer.Length == 3 &&
                    readResult == 3 &&
                    receivedBuffer[0] == (byte)COMMAND.DEVICE_TAG &&
                    receivedBuffer[1] == (byte)COMMAND.DATA_INFO_HEADER)
                {
                    switch (receivedBuffer[2])
                    {
                        case (byte)DEVICE_RESPONSE.SUCCESS:
                            sentOK = true;
                            break;
                        case (byte)DEVICE_RESPONSE.ERROR:
                        default:
                            sentOK = false;
                            break;
                    }
                    Logger.Instance.Log(this, "Device response: " + (DEVICE_RESPONSE)receivedBuffer[2]);
                    if (!sentOK)
                        return;
                }
                else
                {
                    // Process error here
                    Logger.Instance.Log(this, "Device response too much data:  " + readResult + "bytes");
                    return;
                }

                Logger.Instance.Log(this, "Sent  update data info");
                _tcpClient.SendBufferSize = 1024;

                for (int i = 0; i < prog.Pakages.Count; i++)
                {
                    Logger.Instance.Log(this, "Sending update data package " + i);
                    //stream.Write(prog.Info.Data, 0, prog.Info.Data.Length);
                    //stream.Flush();
                    //Thread.Sleep(1000);
                    //Sender.SendBytes(_tcpClient.Client, prog.Pakages[i].Data);
                    //stream.Write(prog.Pakages[i].Data, 0, prog.Pakages[i].Data.Length);

                    byte crc = LetiLib.Security.CRC.ComputeChecksum(prog.Pakages[i].Data);
                    byte[] outBuf = new byte[prog.Pakages[i].Data.Length + 1];
                    Array.Copy(prog.Pakages[i].Data, 0, outBuf, 0, prog.Pakages[i].Data.Length);
                    //if (iTmpData % 10 == 0) crc++;
                    outBuf[prog.Pakages[i].Data.Length] = crc;

                    bWriter.Write(XORCryptor.Encrypt(outBuf));
                    //stream.Flush();
                    //receivedBuffer = ReadFully(stream, 3);
                    //receivedBuffer = bReader.ReadBytes(3);
                    //bReader = new BinaryReader(_tcpClient.GetStream());
                    readResult = bReader.Read(receivedBuffer, 0, 3);
                    bReader.BaseStream.Flush();
                    if (//receivedBuffer.Length == 3 &&
                        readResult == 3 &&
                        receivedBuffer[0] == (byte)COMMAND.DEVICE_TAG &&
                        receivedBuffer[1] == (byte)COMMAND.DATA_PACKAGE_HEADER)
                    {
                        switch (receivedBuffer[2])
                        {
                            case (byte)DEVICE_RESPONSE.SUCCESS:
                                sentOK = true;
                                break;
                            case (byte)DEVICE_RESPONSE.ERROR:
                            default:
                                sentOK = false;
                                break;
                        }
                        Logger.Instance.Log(this, "Device response: " + (DEVICE_RESPONSE)receivedBuffer[2]);
                        if (!sentOK)
                            break;
                    }
                    else
                    {
                        // Process error here
                        Logger.Instance.Log(this, "Device response too much data:  " + readResult + "bytes");
                        break;
                    }
                }


            }
            catch (System.Exception ex)
            {
                Logger.Instance.Log(this, ex.Message);
                return;
            }
        }


        /// <summary>
        /// Reads data from a stream until the end is reached. The
        /// data is returned as a byte array. An IOException is
        /// thrown if any of the underlying IO calls fail.
        /// </summary>
        /// <param name="stream">The stream to read data from</param>
        /// <param name="initialLength">The initial buffer length</param>
        public byte[] ReadFully(Stream stream, int initialLength)
        {
            // If we've been passed an unhelpful initial length, just
            // use 32K.
            if (initialLength < 1)
            {
                initialLength = 32768;
            }

            byte[] buffer = new byte[initialLength];
            int read = 0;

            int chunk;
            while ((chunk = stream.Read(buffer, read, buffer.Length - read)) > 0)
            {
                read += chunk;

                // If we've reached the end of our buffer, check to see if there's
                // any more information
                if (read == buffer.Length)
                {
                    return buffer;
                    int nextByte = stream.ReadByte();

                    // End of stream? If so, we're done
                    if (nextByte == -1)
                    {
                        return buffer;
                    }

                    // Nope. Resize the buffer, put in the byte we've just
                    // read, and continue
                    byte[] newBuffer = new byte[buffer.Length * 2];
                    Array.Copy(buffer, newBuffer, buffer.Length);
                    newBuffer[read] = (byte)nextByte;
                    buffer = newBuffer;
                    read++;
                }
            }
            // Buffer is now too big. Shrink it.
            byte[] ret = new byte[read];
            Array.Copy(buffer, ret, read);
            return ret;
        }

        public void sendPrograms(PlaylistData playlist)
        {
            TaskScheduler uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
            DateTime before = DateTime.Now;
            // Send playlist process
            Task playlistSending = Task.Factory.StartNew(() =>
                {
                    // Send update playlist command to device
                    // reset timeout timer
                    /*
                    DataTimer.AutoReset = false;
                    DataTimer.Stop();
                    DataTimer.Start();
                    Logger.Instance.Log(this,"Sending  update playlist request...");
                    bDataRequested = false;
                    // send data request header
                    Sender.vSendUpdatePlaylistRequest(_tcpClient.Client);
                    while (!bDataRequested)
                    {
                        if (bDataRequestedTimeout)
                        {
                            Logger.Instance.Log(this,"      > Sending  update playlist request cancel because of timeout!");
                            return;
                        }
                    }

                    DataTimer.Stop();
                    bDataInfoSent = false;
                    Logger.Instance.Log(this,"Sending playlist info...");
                    Sender.vSendPlaylistInfo(_tcpClient.Client, playlist.Info);
                    while (!bDataInfoSent)
                    {
                        if (bDataRequestedTimeout)
                        {
                            Logger.Instance.Log(this,"     > Sending  playlist data info canceled because of timeout!");
                            return;
                        }
                    }

                    foreach (ProgramData program in playlist.packages)
                    {
                        sendOneProgramInPlaylist(program);
                    }
                     * */
                },
                TaskCreationOptions.LongRunning
            );

            // finishes after running
            playlistSending.ContinueWith(task =>
            {
                // detects operation duration
                //TimeSpan duration = DateTime.Now - before;
                //TimeSpan perPixel = new TimeSpan(duration.Ticks);
                MessageBox.Show("Send program done!");
            }, uiScheduler);
        }

        private void sendOneProgramInPlaylist(ProgramData program)
        {
            int totalSteps = 2 + program.Pakages.Count;
            try
            {
                /*
                DataTimer.Stop();
                bDataInfoSent = false;
                Logger.Instance.Log(this,"Sending  update data info...");
                Sender.vSendUpdateDataInfo(_tcpClient.Client, program.Info);
                while (!bDataInfoSent)
                {
                    if (bDataRequestedTimeout)
                    {
                        Logger.Instance.Log(this,"     > Sending  update data info canceled because of timeout!");
                        return;
                    }
                }
                //bwSimulator.ReportProgress(2 / totalSteps * 100);
                Logger.Instance.Log(this,"Sent  update data info");
                for (int i = 0; i < program.Pakages.Count; i++)
                {
                    Logger.Instance.Log(this,"Sending update data package " + i);
                    bFirmwarePackageSent = false;
                    Sender.vSendUpdateDataPackage(_tcpClient.Client, program.Pakages[i]);
                    DataTimer.Stop();
                    while (!bFirmwarePackageSent)
                    {
                        if (bFirmwarePackageSentError_checksum)
                        {
                            if (bDataRequestedTimeout) return;
                            Sender.vSendUpdateDataPackage(_tcpClient.Client, program.Pakages[i]);
                            bFirmwarePackageSentError_checksum = false;
                        }
 
                    }
                    //bwSimulator.ReportProgress(i + 3 / totalSteps * 100);
                }
                    */

            }
            catch (System.Exception ex)
            {
                Logger.Instance.Log(this,ex.Message);
                //MessageBox.Show("Error while sending firmware to selected client\r\n" + ex.Message);
                return;
            }
        }

        public void sendPredefined(ProgramData prog, ushort order,ushort imgCode)
        {
            Logger.Instance.Log(this,"Sending  update request...");

            Sender.vSendUpdatePredefinedRequest(_tcpClient.Client, order, imgCode);         
        }
        public string logContent;
        private void Log()
        {

        }

        // Update firmware
        public void sendFirmware(BlackboxFirmware firmware)
        {
            //TaskScheduler uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
            DateTime before = DateTime.Now;
            // Send playlist process
            Task playlistSending = Task.Factory.StartNew(() =>
            {
                // Send update firmware command to device
                // reset timeout timer
                DataTimer.AutoReset = false;
                DataTimer.Stop();
                DataTimer.Start();
                Logger.Instance.Log(this,"Sending  update firmware request...");
                bDataRequested = false;
                // send data request header
                Sender.vSendUpdateFirmwareRequest(_tcpClient.Client);
                while (!bDataRequested)
                {
                    if (bDataRequestedTimeout)
                    {
                        Logger.Instance.Log(this,"      > Sending  update firmware request cancel because of timeout!");
                        return;
                    }
                }

                DataTimer.Stop();
                bDataInfoSent = false;
                Logger.Instance.Log(this,"Sending firmware info...");
                Sender.vSendUpdateFirmwareInfo(_tcpClient.Client, firmware.Info);
                while (!bDataInfoSent)
                {
                    if (bDataRequestedTimeout)
                    {
                        Logger.Instance.Log(this,"     > Sending  firmware data info canceled because of timeout!");
                        return;
                    }
                }

                Logger.Instance.Log(this,"Sent  firmware data info");
                for (int i = 0; i < firmware.Pakages.Count; i++)
                {
                    Logger.Instance.Log(this,"Sending update data package " + i);
                    bFirmwarePackageSent = false;
                    Sender.vSendUpdateFirmwarePackage(_tcpClient.Client, firmware.Pakages[i]);
                    DataTimer.Stop();
                    while (!bFirmwarePackageSent)
                    {
                        if (bFirmwarePackageSentError_checksum)
                        {
                            if (bDataRequestedTimeout) return;
                            Sender.vSendUpdateFirmwarePackage(_tcpClient.Client, firmware.Pakages[i]);
                            bFirmwarePackageSentError_checksum = false;
                        }

                    }
                    //bwSimulator.ReportProgress(i + 3 / totalSteps * 100);
                }
                Logger.Instance.Log(this, "Sending firmware success!");
            },
                TaskCreationOptions.LongRunning
            );
        }
        
        // Update font
        public void sendFontData(FontFirmware firmware)
        {
            //TaskScheduler uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
            DateTime before = DateTime.Now;
            // Send playlist process
            Task playlistSending = Task.Factory.StartNew(() =>
            {
                // Send update firmware command to device
                // reset timeout timer
                DataTimer.AutoReset = false;
                DataTimer.Stop();
                DataTimer.Start();
                Logger.Instance.Log(this, "Sending  update font request...");
                bDataRequested = false;
                // send data request header
                Sender.vSendUpdateFontRequest(_tcpClient.Client);
                while (!bDataRequested)
                {
                    if (bDataRequestedTimeout)
                    {
                        Logger.Instance.Log(this, "      > Sending  update font request cancel because of timeout!");
                        return;
                    }
                }

                DataTimer.Stop();
                bDataInfoSent = false;
                Logger.Instance.Log(this, "Sending firmware info...");
                Sender.vSendUpdateFontInfo(_tcpClient.Client, firmware.Info);
                while (!bDataInfoSent)
                {
                    if (bDataRequestedTimeout)
                    {
                        Logger.Instance.Log(this, "     > Sending  font data info canceled because of timeout!");
                        return;
                    }
                }

                Logger.Instance.Log(this, "Sent  font data info");
                for (int i = 0; i < firmware.Pakages.Count; i++)
                {
                    Logger.Instance.Log(this, "Sending update data package " + i);
                    bFirmwarePackageSent = false;
                    Sender.vSendUpdateFontPackage(_tcpClient.Client, firmware.Pakages[i]);
                    DataTimer.Stop();
                    while (!bFirmwarePackageSent)
                    {
                        if (bFirmwarePackageSentError_checksum)
                        {
                            if (bDataRequestedTimeout) return;
                            Sender.vSendUpdateFontPackage(_tcpClient.Client, firmware.Pakages[i]);
                            bFirmwarePackageSentError_checksum = false;
                        }

                    }
                    //bwSimulator.ReportProgress(i + 3 / totalSteps * 100);
                }
                Logger.Instance.Log(this, "Sending font success!");
            },
                TaskCreationOptions.LongRunning
            );
        }
        
        #endregion
        
        #region | Process received data |
        // tungpt added >>
        // variable for UPDATE_STATE firmware state
        bool bDataRequested = false;
        bool bDataRequestedTimeout = false;
        bool bDataInfoSent = false;
        bool bFirmwarePackageSent = false;
        bool bFirmwarePackageSentError_checksum = false;
        
        public event DataTimeoutHandler DataTimeout;
        public System.Timers.Timer DataTimer = new System.Timers.Timer(PACKAGE_TIMEOUT);
        // <<
        public void processData(byte[] btReceive)
        {
            if (Receiver.bAcceptCmdHeader(btReceive))
            {
                COMMAND result;
                int index, packageLength;

                // tungpt added >>
                if (Receiver.bAcceptDataReqCmd(btReceive) || Receiver.bAcceptPlaylistReqCmd(btReceive)
                    || Receiver.bAcceptFirmwareReqCmd(btReceive) || Receiver.bAcceptFontReqCmd(btReceive))
                {
                    Logger.Instance.Log(this,"     > Receive req update");
                    bDataRequested = true;
                    // start data timeout timer
                    bDataRequestedTimeout = false;
                    DataTimer.AutoReset = false;
                    //DataTimer.Stop();
                    //DataTimer.Start();
                    
                    //Sender.vSendAckCmd(handler, DEFS.WMA_ACK);
                    //Sender.vSendUpdateFirmwareInfo(handler,parser.data.Length,parser.Offset);
                }
                else if (Receiver.bAcceptDataInfo(btReceive) || Receiver.bAcceptPlaylistInfo(btReceive)
                    || Receiver.bAcceptFirmwareInfo(btReceive) || Receiver.bAcceptFontInfo(btReceive))
                {
                    Logger.Instance.Log(this,"     > Receive req info");
                    bDataInfoSent = true;
                    // start data timeout timer
                    
                    bDataRequested = true;
                    // start data timeout timer
                    bDataRequestedTimeout = false;
                    DataTimer.AutoReset = false;
                    //DataTimer.Stop();
                    //DataTimer.Start();
                    
                    //Sender.vSendAckCmd(handler, DEFS.WMA_ACK);
                    //Sender.vSendUpdateFirmwareInfo(handler,parser.data.Length,parser.Offset);
                    
                }
                else if (Receiver.bAcceptDataError(btReceive) || Receiver.bAcceptPlaylistError(btReceive)
                    || Receiver.bAcceptFirmwareError(btReceive) || Receiver.bAcceptFontPackageError(btReceive))
                {
                    Logger.Instance.Log(this, "     > Receive req info Error");
                    bDataInfoSent = false;
                    // start data timeout timer

                    bDataRequested = true;
                    // start data timeout timer
                    bDataRequestedTimeout = true;
                    DataTimer.AutoReset = false;
                    //DataTimer.Stop();
                    //DataTimer.Start();

                    //Sender.vSendAckCmd(handler, DEFS.WMA_ACK);
                    //Sender.vSendUpdateFirmwareInfo(handler,parser.data.Length,parser.Offset);

                }
                else if (Receiver.bAcceptDataPackage(btReceive) || Receiver.bAcceptFirmwarePackage(btReceive)
                    || Receiver.bAcceptFontPackage(btReceive))
                {
                    Logger.Instance.Log(this,"     > Receive req package");
                    bFirmwarePackageSent = true;

                    bDataRequested = true;
                    // start data timeout timer
                    DataTimer.AutoReset = false;
                    bDataRequestedTimeout = false;
                    //DataTimer.Stop();
                    //DataTimer.Start();
                    
                    //Sender.vSendAckCmd(handler, DEFS.WMA_ACK);
                    //Sender.vSendUpdateFirmwareInfo(handler,parser.data.Length,parser.Offset);
                    
                }
                //else if (Receiver.bAcceptFirmwarePackageIDError(btReceive))
                //{
                //    Logger.Instance.Log(this,"     > Receive req package ID");
                //    bFirmwarePackageSent = true;

                //    bFirmwareRequested = true;
                //    // start data timeout timer
                //    DataTimer.AutoReset = false;
                //    bFirmwareRequestedTimeout = false;
                //    //DataTimer.Stop();
                //    //DataTimer.Start();

                //    //Sender.vSendAckCmd(handler, DEFS.WMA_ACK);
                //    //Sender.vSendUpdateFirmwareInfo(handler,parser.data.Length,parser.Offset);

                //}
                    
                else if (Receiver.bAcceptDataPackageError(btReceive) || Receiver.bAcceptFirmwarePackageError(btReceive)
                    || Receiver.bAcceptDataPackageIDError(btReceive) || Receiver.bAcceptFirmwarePackageIDError(btReceive))
                {
                    bFirmwarePackageSentError_checksum = true;

                    bDataRequested = true;
                    // start data timeout timer
                    DataTimer.AutoReset = false;
                    bDataRequestedTimeout = false;
                    //DataTimer.Stop();
                    //DataTimer.Start();
                    Logger.Instance.Log(this,"Send req error");
                    //Sender.vSendAckCmd(handler, DEFS.WMA_ACK);
                    //Sender.vSendUpdateFirmwareInfo(handler,parser.data.Length,parser.Offset);
                    
                }
                    /*
                else if (Receiver.bAcceptPlaylistReqCmd(btReceive) || Receiver.bAcceptFirmwareReqCmd(btReceive))
                {
                    Logger.Instance.Log(this,"     > Receive req playlist update");
                    bDataRequested = true;
                    // start data timeout timer
                    bDataRequestedTimeout = false;
                    DataTimer.AutoReset = false;
                    //DataTimer.Stop();
                    //DataTimer.Start();

                    //Sender.vSendAckCmd(handler, DEFS.WMA_ACK);
                    //Sender.vSendUpdateFirmwareInfo(handler,parser.data.Length,parser.Offset);
                }
                else if (Receiver.bAcceptPlaylistInfo(btReceive) || Receiver.bAcceptFirmwareInfo(btReceive))
                {
                    Logger.Instance.Log(this,"     > Receive playlist info");
                    bDataInfoSent = true;
                    // start data timeout timer
                    
                    bDataRequested = true;
                    // start data timeout timer
                    bDataRequestedTimeout = false;
                    DataTimer.AutoReset = false;
                    //DataTimer.Stop();
                    //DataTimer.Start();
                    
                    //Sender.vSendAckCmd(handler, DEFS.WMA_ACK);
                    //Sender.vSendUpdateFirmwareInfo(handler,parser.data.Length,parser.Offset);
                    
                }
                else if (Receiver.bAcceptFirmwarePackage(btReceive))
                {
                    Logger.Instance.Log(this,"     > Receive req firmware package");
                    bDataRequested = true;
                    // start data timeout timer
                    bDataRequestedTimeout = false;
                    DataTimer.AutoReset = false;
                    //DataTimer.Stop();
                    //DataTimer.Start();

                    //Sender.vSendAckCmd(handler, DEFS.WMA_ACK);
                    //Sender.vSendUpdateFirmwareInfo(handler,parser.data.Length,parser.Offset);
                }*/
                else if (Receiver.bAcceptIMEI(btReceive))
                {
                    Logger.Instance.Log(this,"     > Same IMEI, disconnect old connection");
                    this.close();
                }
                
                else
                {
                    // TODO: Process byte data.
                    return ;
                }
            }
            return ;
        }
        #endregion
        
        #region | Simulator event |
        //public event ProgresChangedHandler ProgressChanged;
        public BackgroundWorker bwSimulator;
        ProgramData programToSent;
        int delay = 0;
        static Random ran = new Random();
        public void startProgramSim()
        {
            // in simulation mode, we send data after random time
            delay = (ran.Next(4)+1) * 1000;
            bwSimulator.RunWorkerAsync();
        }
        public void stopProgramSim()
        {
            if (bwSimulator.IsBusy)
            {

                // Notify the worker thread that a cancel has been requested.

                // The cancel will not actually happen until the thread in the

                // DoWork checks the m_oWorker.CancellationPending flag. 

                bwSimulator.CancelAsync();
            }
        }
        /// <summary>
        /// Time consuming operations go here </br>
        /// i.e. Database operations,Reporting
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void bwSimulatorDoWork(object sender, DoWorkEventArgs e)
        {
            // The sender is the BackgroundWorker object we need it to
            // report progress and check for cancellation.
            //NOTE : Never play with the UI thread here...
#if TEST_PROGRESS_UPDATE
            for (int i = 0; i < 100; i++)
            {

                Thread.Sleep(delay);
                // Periodically report progress to the main thread so that it can
                // update the UI.  In most cases you'll just need to send an
                // integer that will update a ProgressBar                    
                bwSimulator.ReportProgress(i);
                // Periodically check if a cancellation request is pending.
                // If the user clicks cancel the line
                // m_AsyncWorker.CancelAsync(); if ran above.  This
                // sets the CancellationPending to true.
                // You must check this flag in here and react to it.
                // We react to it by setting e.Cancel to true and leaving
                if (bwSimulator.CancellationPending)
                {
                    // Set the e.Cancel flag so that the WorkerCompleted event
                    // knows that the process was cancelled.
                    e.Cancel = true;
                    //bwSimulator.ReportProgress(0);
                    return;
                }
            }
            //Report 100% completion on operation completed
            bwSimulator.ReportProgress(100);
#else
            int totalSteps = 2 + programToSent.Pakages.Count;
            try
            {
                DataTimer.AutoReset = false;
                DataTimer.Stop();
                DataTimer.Start();
                Logger.Instance.Log(this,"Sending  update data request...");
                bDataRequested = false;
                Sender.vSendUpdateDataRequest(_tcpClient.Client, 1);
                while (!bDataRequested)
                {
                    if (bDataRequestedTimeout)
                    {
                        Logger.Instance.Log(this,"      > Sending  update data request cancel because of timeout!");
                        return;
                    }
                    if (bwSimulator.CancellationPending)
                    {
                        // Set the e.Cancel flag so that the WorkerCompleted event
                        // knows that the process was cancelled.
                        e.Cancel = true;
                        //bwSimulator.ReportProgress(0);
                        return;
                    }
                }
                // update progress bar
                bwSimulator.ReportProgress(1 / totalSteps * 100);

                DataTimer.Stop();
                bDataInfoSent = false;
                Logger.Instance.Log(this,"Sending  update data info...");
                Sender.vSendUpdateDataInfo(_tcpClient.Client, programToSent.Info);
                while (!bDataInfoSent)
                {
                    if (bDataRequestedTimeout)
                    {
                        Logger.Instance.Log(this,"     > Sending  update data info canceled because of timeout!");
                        return;
                    }
                    if (bwSimulator.CancellationPending)
                    {
                        // Set the e.Cancel flag so that the WorkerCompleted event
                        // knows that the process was canceled.
                        e.Cancel = true;
                        //bwSimulator.ReportProgress(0);
                        return;
                    }
                }
                bwSimulator.ReportProgress(2 / totalSteps * 100);
                Logger.Instance.Log(this,"Sent  update data info");
                if (bwSimulator.CancellationPending)
                {
                    // Set the e.Cancel flag so that the WorkerCompleted event
                    // knows that the process was canceled.
                    e.Cancel = true;
                    //bwSimulator.ReportProgress(0);
                    return;
                }
                for (int i = 0; i < programToSent.Pakages.Count; i++)
                {
                    Logger.Instance.Log(this,"Sending update data package " + i);
                    bFirmwarePackageSent = false;
                    Sender.vSendUpdateDataPackage(_tcpClient.Client, programToSent.Pakages[i]);
                    DataTimer.Stop();
                    while (!bFirmwarePackageSent)
                    {
                        if (bFirmwarePackageSentError_checksum)
                        {
                            if (bDataRequestedTimeout) return;
                            Sender.vSendUpdateDataPackage(_tcpClient.Client, programToSent.Pakages[i]);
                            bFirmwarePackageSentError_checksum = false;
                        }
                        if (bwSimulator.CancellationPending)
                        {
                            // Set the e.Cancel flag so that the WorkerCompleted event
                            // knows that the process was cancelled.
                            e.Cancel = true;
                            //bwSimulator.ReportProgress(0);
                            return;
                        }
                    }
                    bwSimulator.ReportProgress(i+3 / totalSteps * 100);
                }

                
            }
            catch (System.Exception ex)
            {
                Logger.Instance.Log(this,ex.Message);
                //MessageBox.Show("Error while sending firmware to selected client\r\n" + ex.Message);
                return;
            }
#endif
        }

        void DataTimer_Elapsed(object sender, ElapsedEventArgs e)
        {
            // TODO: sent events to main form
            #if TEST_OFFLINE
            #else
                close();
            #endif
            bDataRequestedTimeout = true;
            if (DataTimeout != null)
            DataTimeout(this, new EventArgs());
        }
        #endregion

        public void SendBusData(string busContent)
        {
            //Sender.UpdateBusData(_tcpClient.Client, busContent);
            ushort pageID = 0;

            TABLE_POPUP newPopup = new TABLE_POPUP();
            foreach (POPUP p in pages[pageID].PopUps)
            {
                if (p.DataType == POPUP_TYPE.TABLE)
                {
                    newPopup = (TABLE_POPUP)p;
                    break;
                }
            }
            newPopup.Table.Rows = 2;
            newPopup.Table.Columns = 4;
            newPopup.Table.ContentData = busContent;

            bool ret = Sender.SendUpdatePopupCmd(_tcpClient.Client, pages[pageID].Header.ID, newPopup.ID, newPopup);
        }

        public void SendActiveImage(ushort pageID, ushort popupID, ushort imgID, byte playOrder = 0)
        {
            Sender.SendActiveImage(_tcpClient.Client,pageID, popupID, playOrder,imgID);
            
        }

        public void SendActivePage(ushort pageID)
        {
            Sender.SendActivePage(_tcpClient.Client, pageID);
        }

        public void SendCreatePageCmd(LETIPage page)
        {
            bool r = Sender.SendCreatePageCmd(_tcpClient.Client, page);
            pages.Add(page);
        }

        public void SendUpdatePageHeaderCmd(ushort pageID, LETIPageHeader pageHeader)
        {
            bool r = Sender.SendUpdatePageHeaderCmd(_tcpClient.Client, pageID, pageHeader);
            pages[pageID].Header = pageHeader;
        }

        public void SendDeletePageCmd(ushort pageID)
        {
            Sender.SendDeletePage(_tcpClient.Client, pageID);
            pages.RemoveAt(pageID);
        }

        public void SendGetActivePageCmd()
        {
            Sender.SendGetActivePageCmd(_tcpClient.Client);
        }

        public void SendInsertPopupCmd(ushort pageID, ushort popupID, POPUP popupData)
        {
            bool r = Sender.SendInsertPopupCmd(_tcpClient.Client, pageID, popupID, popupData);
        }

        public void SendUpdatePopupCmd(ushort pageID, ushort popupID, POPUP popupData)
        {
            bool r = Sender.SendUpdatePopupCmd(_tcpClient.Client, pageID, popupID, popupData);
        }

        public void SendDeletePopupCmd(ushort pageID, ushort popupID)
        {
            bool r = Sender.SendDeletePopupCmd(_tcpClient.Client, pageID, popupID);
        }

        public void SendPageInfoCmd(ushort pageID)
        {
            bool r = Sender.SendPageInfoCmd(_tcpClient.Client, pageID);
        }

        public void SendDeleteImageCmd(ushort imgageID)
        {
            bool r = Sender.SendDeleteImageCmd(_tcpClient.Client, imgageID);
        }

        public void SendSetPredefineCmd(ushort imgageID,byte romID)
        {
            bool r = Sender.SendSetPredefineCmd(_tcpClient.Client, imgageID, romID);
        }

        public void SendDeleteAllImageCmd()
        {
            bool r = Sender.SendDeleteAllImageCmd(_tcpClient.Client);
        }

        public void SendGetImageListCmd()
        {
            bool r = Sender.SendGetImageListCmd(_tcpClient.Client);
        }

        public void SendUpdateImageCmd(ushort pageID, ushort popupID, byte playOrder, IMAGE imageData)
        {
            bool r = Sender.SendUpdateImageCmd(_tcpClient.Client, pageID, popupID, playOrder, imageData);
        }

        public void SendInsertImageToPlaylistCmd(ushort pageID, ushort popupID, IMAGE imageData)
        {
            bool r = Sender.SendInsertImageToPlaylistCmd(_tcpClient.Client, pageID, popupID, imageData);
        }

        public void SendDeleteImageFromPlaylistCmd(ushort pageID, ushort popupID, byte playOrder)
        {
            bool r = Sender.SendDeleteImageFromPlaylistCmd(_tcpClient.Client, pageID, popupID, playOrder);
        }

        public void SendGetStatusCmd()
        {
            bool r = Sender.SendGetStatusCmd(_tcpClient.Client);
        }

        public void SendCaptureScreenCmd()
        {
            bool r = Sender.SendScreenCaptureCmd(_tcpClient.Client);
        }

        public void SendRebootCmd()
        {
            bool r = Sender.SendRebootCmd(_tcpClient.Client);
        }

        public void SendClearScreenCmd()
        {
            ushort bufSize = 2;
            byte[] data = new byte[bufSize];
            data[0] = (byte)COMMAND.SERVER_TAG;
            data[1] = (byte)COMMAND.CMD_CLEAR_LED;

            // send buffer data
            bool r =  Sender.SendBytes(_tcpClient.Client, data);

            // Get the stream
            NetworkStream stream = _tcpClient.GetStream();
            Byte[] revdata = new Byte[3];

            // Read the first batch of the TcpServer response bytes.
            int bytes = stream.Read(data, 0, data.Length);
            
        }

        public void SendScanScreenCmd()
        {
            ushort bufSize = 4;
            byte[] data = new byte[bufSize];
            data[0] = (byte)COMMAND.SERVER_TAG;
            data[1] = (byte)COMMAND.CMD_SCAN_LED;
            BitConverter.GetBytes((ushort)5).CopyTo(data, 2);
            // send buffer data
            bool r = Sender.SendBytes(_tcpClient.Client, data);
        }

        public void SendCheckLedCmd()
        {
            ushort bufSize = 4;
            byte[] data = new byte[bufSize];
            data[0] = (byte)COMMAND.SERVER_TAG;
            data[1] = (byte)COMMAND.CMD_CHECK_LED;
            BitConverter.GetBytes((ushort)5).CopyTo(data, 2);
            // send buffer data
            bool r = Sender.SendBytes(_tcpClient.Client, data);
        }

        
    }
    /*
    public class ProgressChangedEventArgs : EventArgs
    {
        int value;

        public ProgressChangedEventArgs(int val):base()
        {
            value = val;
        }
    }
     * */
    public class StateObject
    {
        // Client  socket.
        //public Socket workSocket = null;
        // Size of receive buffer.
        public const int BufferSize = 1024;
        // Receive buffer.
        public byte[] buffer = new byte[BufferSize];
        // Received data string.

        public StringBuilder sb = new StringBuilder();

        public int index = -1;
    }
}
